package com.valotas.taglibs.scripting.groovy;


import java.util.logging.Level;
import java.util.logging.Logger;

import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.servlet.ServletCategory;
import groovy.util.GroovyScriptEngine;

import javax.servlet.jsp.JspException;

import org.codehaus.groovy.runtime.GroovyCategorySupport;

import com.valotas.taglibs.scripting.InvokeScriptTag;

/**
 * A version of the InvokeScriptTag that handles groovy scripts witch
 * should be placed in the "/WEB-INF/scriptags/" folder of the web
 * application.
 * 
 * @author valotas
 * @version , 09 Jan 2009
 *
 */
public class InvokeGroovyScriptTag extends InvokeScriptTag {
	private static final long serialVersionUID = 357495079240775255L;
	private static final Logger logger = Logger.getLogger(InvokeGroovyScriptTag.class.getName());
	private static final String GROOVY_SCRIPT_ENGINE =
		InvokeGroovyScriptTag.class.getName() + "#" + GroovyScriptEngine.class.getSimpleName();


	public void setName(String name) {
		/*
		 * We'll be calling groovy script's and that's why we have to make
		 * sure that the name ends with ".groovy"
		 */
		if (!name.endsWith(".groovy")) this.name = name + ".groovy";
		else this.name = name;
	}


	@Override
	public int doEndTag() throws JspException {
		GroovyScriptEngine gse = getGroovyScriptEngine();

		final Binding bind = new TagBinding(attr, pageContext);

		//Try to run the script:
		try {
			Closure closure = new Closure(gse) {
				public Object call() {
					try {
						return ((GroovyScriptEngine) getDelegate()).run(name, bind);
					}
					catch (Exception e) {
						if (logger.isLoggable(Level.WARNING)) logger.log(Level.WARNING, String.format("%s couldn't be executed completely", name), e);
						return new RuntimeException(e);
					}
				}
			};

			GroovyCategorySupport.use(ServletCategory.class, closure);
		}
		catch (Exception e) {
			String msg = String.format("Could not execute script '%s'", name);
			if (logger.isLoggable(Level.WARNING)) logger.log(Level.WARNING, msg, e);
			throw new JspException(msg, e);
		}

		return super.doEndTag();
	}

	
	/**
	 * Tries to get a {@link GroovyScriptEngine} from the application's
	 * context. If that now exists creates one saves it to the application's
	 * context for future use and then return it.
	 * 
	 * @return {@link GroovyScriptEngine}
	 */
	private GroovyScriptEngine getGroovyScriptEngine() {
		GroovyScriptEngine gse =
			(GroovyScriptEngine) pageContext.getServletContext().getAttribute(GROOVY_SCRIPT_ENGINE);
		if (gse == null) {
			synchronized (logger) { //We use the logger as a lock
				gse = (GroovyScriptEngine) pageContext.getServletContext().getAttribute(GROOVY_SCRIPT_ENGINE);
				if (gse == null) {
					gse = new GroovyScriptEngine(new SimpleResourceConnector(pageContext.getServletContext()));
					pageContext.getServletContext().setAttribute(GROOVY_SCRIPT_ENGINE, gse);
					
					if (logger.isLoggable(Level.INFO)) logger.log(Level.INFO, String.format("%s has been initalized and save as '%s' for future use", gse, GROOVY_SCRIPT_ENGINE));
				}
			}
		}

		return gse;
	}
}
